package com.ruoyi.business.payment.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.business.payment.bean.BasePaymentResponse;
import com.ruoyi.business.payment.config.PaymentConfig;
import com.ruoyi.business.payment.service.ITRegisterPaymentOrderService;
import com.ruoyi.business.payment.service.IThirdPaymentService;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


@Service
@Slf4j
public class ThirdPaymentServiceImpl implements IThirdPaymentService {

    @Autowired
    private PaymentConfig paymentConfig;

    @Autowired
    private ITRegisterPaymentOrderService paymentOrderService;

    /**
     * 提交预支付订单
     *
     * @param requestData 请求参数
     * @return json
     * @throws Exception e
     */
    @Override
    public BasePaymentResponse submitPrePayOrder(String requestData) throws Exception {
        return getBasePaymentResponse(requestData, "payOrder");
    }

    /**
     * 提交退款申请
     *
     * @param requestData 请求参数
     * @return
     * @throws Exception
     */
    @Override
    public BasePaymentResponse submitReturnOrder(String requestData) throws Exception {
        return getBasePaymentResponse(requestData, "refundOrder");
    }

    /**
     * 关闭订单
     *
     * @param requestData 请求参数
     * @return
     * @throws Exception
     */
    @Override
    public BasePaymentResponse submitCloseOrder(String requestData) throws Exception {
        return getBasePaymentResponse(requestData, "closeOrder");
    }

    /**
     * 支付完成回调
     *
     * @return json
     */
    @Override
    public JSONObject paymentCallback() {
        HttpServletRequest request = ServletUtils.getRequest();
        StringBuilder body = new StringBuilder();
        String secretKey = paymentConfig.getAppkey().substring(2, 18);
        try (BufferedReader reader = request.getReader()) {
            String line;
            while ((line = reader.readLine()) != null) {
                body.append(line);
            }
            log.info("【支付完成回调】 原始加密报文,{}", body);
            JSONObject result = JSON.parseObject(body.toString());
            String rspDataStr = result.getString("data");
            String decryptData = decrypt(rspDataStr, secretKey);
            decryptData = new String(Base64.decodeBase64(decryptData.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
            log.info("【支付完成回调】返回解密后数据,{}", decryptData);
            JSONObject decryptJson = JSONObject.parseObject(decryptData);
            Long busNo = decryptJson.getLong("busNo");
            String channelOrderNo = decryptJson.getString("channelOrderNo");
            String payChannelOrderNo = decryptJson.getString("channelOrderNo");
            BigDecimal amount = decryptJson.getBigDecimal("amount");
            String payTime = decryptJson.getString("payDateTime");
            Date parseDate = DateUtils.parseDate(payTime, "yyyyMMddHHmmssSSS");
            boolean isOnline = true;
            JSONArray payChannelDetail = decryptJson.getJSONArray("payChannelDetail");
            for (int i = 0; i < payChannelDetail.size(); i++) {
                String payChannelCode = payChannelDetail.getJSONObject(i).getString("payChannelCode");
                payChannelOrderNo = payChannelDetail.getJSONObject(i).getString("payChannelOrderNo");
                if(payChannelCode.contains("OFFLINE")){
                    isOnline = false;
                    break;
                }
            }
            paymentOrderService.handlePaymentCallback(busNo, channelOrderNo, payChannelOrderNo, amount, parseDate, isOnline);

            JSONObject returnObj = new JSONObject();
            returnObj.put("result", "S0000");
            returnObj.put("message", encrypt("操作成功", secretKey));
            return returnObj;
        } catch (Exception e) {
            JSONObject returnObj = new JSONObject();
            returnObj.put("result", "S0001");
            try {
                returnObj.put("message", encrypt("操作失败，" + e.getMessage(), secretKey));
            } catch (Exception ex) {
                returnObj.put("message", "操作失败，" + e.getMessage());
            }
            log.error("支付完成回调失败,{}", e.getMessage());
            return returnObj;
        }

    }

    /**
     * 退款回调
     *
     * @return json
     */
    @Override
    public JSONObject refundCallback() {
        HttpServletRequest request = ServletUtils.getRequest();
        StringBuilder body = new StringBuilder();
        String secretKey = paymentConfig.getAppkey().substring(2, 18);
        try (BufferedReader reader = request.getReader()) {
            String line;
            while ((line = reader.readLine()) != null) {
                body.append(line);
            }
            log.info("【退款完成回调】 原始加密报文,{}", body);
            JSONObject result = JSON.parseObject(body.toString());
            String rspDataStr = result.getString("data");
            String decryptData = decrypt(rspDataStr, secretKey);
            decryptData = new String(Base64.decodeBase64(decryptData.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
            log.info("【退款完成回调】返回解密后数据,{}", decryptData);
            JSONObject decryptJson = JSONObject.parseObject(decryptData);

            String busNo = decryptJson.getString("busNo");
            String refundBusNo = decryptJson.getString("refundBusNo");
            String channelRefundNo = decryptJson.getString("channelRefundNo");
            BigDecimal refundAmount = decryptJson.getBigDecimal("refundAmount");
            String refundTime = decryptJson.getString("refundDateTime");
            Date parseDate = DateUtils.parseDate(refundTime, "yyyyMMddHHmmssSSS");
            paymentOrderService.handleRefundCallback(busNo, refundBusNo, channelRefundNo, refundAmount, parseDate);

            JSONObject returnObj = new JSONObject();
            returnObj.put("result", "S0000");
            returnObj.put("message", encrypt("操作成功", secretKey));
            return returnObj;
        } catch (Exception e) {
            JSONObject returnObj = new JSONObject();
            returnObj.put("result", "S0001");
            try {
                returnObj.put("message", encrypt("操作失败，" + e.getMessage(), secretKey));
            } catch (Exception ex) {
                returnObj.put("message", "操作失败，" + e.getMessage());
            }
            log.error("退款回调失败,{}", e.getMessage());
            return returnObj;
        }

    }

    /**
     * 支付中心接口调用
     *
     * @param data 请求数据
     * @param serviceName 服务名称
     * @return 返回参数
     * @throws Exception e
     */
    private BasePaymentResponse getBasePaymentResponse(String data, String serviceName) throws Exception {
        if(!paymentConfig.isOpenPay()){
            log.info("未启用支付中心调用");
            return new BasePaymentResponse();
        }
        //接口地址
        String url = paymentConfig.getServiceApi() + "/" + serviceName;
        log.info("支付中心接口: {},调用参数：{}", url, data);
        //应用账号
        String appID = paymentConfig.getAppid();
        //密钥 签名私钥key
        String appKey = paymentConfig.getAppkey();
        //版本号
        String version = paymentConfig.getVersion();
        //唯一值 请求随机标识
        String noise = UUID.randomUUID().toString().replaceAll("-", "");
        // 1.对data串Base64编码
        data = Base64.encodeBase64String(data.getBytes(StandardCharsets.UTF_8));

        //2. 做 AES/ECB/PKCS5Padding 返回16进制字符串
        String AESData = encrypt(data, appKey.substring(2, 18));

        //3. 生成sign = SHA512(appID+AESData+noise+version+appKey)返回16进制字符串
        String sign = sign(appID + AESData + noise + version + appKey);
        // 4、最终请求串
        Map<String, String> map = new HashMap<>();
        map.put("appid", appID);
        map.put("data", AESData);
        map.put("noise", noise);
        map.put("sign", sign);
        map.put("version", version);
        try {
            HttpPost httpPost = new HttpPost(url);
            CloseableHttpClient client = HttpClients.createDefault();
            StringEntity entity = new StringEntity(new ObjectMapper().writeValueAsString(map), "utf-8");
            entity.setContentEncoding("UTF-8");
            entity.setContentType("application/json");
            httpPost.setEntity(entity);
            HttpResponse resp = client.execute(httpPost);
            if (resp.getStatusLine().getStatusCode() == 200) {
                String rev = EntityUtils.toString(resp.getEntity());
                //1. 返回结果json转对象或则map
                log.info("返回串--》{}", rev);
                Map result = new ObjectMapper().readValue(rev, Map.class);
                String rdata = result.get("data").toString();
                String rnoise = result.get("noise").toString();
                // 2.返回结果验证安全
                String ysign = sign(paymentConfig.getAppid() + rdata + rnoise + version + paymentConfig.getAppkey());

                String rsign = result.get("sign").toString();
                log.info("验签-》{}", (org.apache.commons.lang3.StringUtils.equals(rsign, ysign)));
                // 3. 做 AES/ECB/PKCS5Padding 解密返回结果明文
                String restData = decrypt(rdata, appKey.substring(2, 18));
                log.info("返回业务数据--》{}", restData);
                String decodeRestData = new String(Base64.decodeBase64(restData), StandardCharsets.UTF_8);
                log.info("业务信息Base64解码--》{}", decodeRestData);
                JSONObject rspObj = null;
                try {
                    rspObj = JSONObject.parseObject(decodeRestData);
                } catch (Exception e) {
                    throw new BaseException(decodeRestData);
                }
                if (!rspObj.getString("result").equals("S0000")) {
                    throw new BaseException(rspObj.getString("message"));
                }
                return JSONObject.parseObject(decodeRestData, BasePaymentResponse.class);
            } else {
                throw new BaseException("支付中心调用失败");
            }
        } catch (Exception e) {
            throw new BaseException(e.getMessage());
        }
    }

    /**
     * 数据签名
     */
    public String sign(String input) throws NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("SHA-512");
        digest.update(input.getBytes());
        byte[] byteBuffer = digest.digest();
        return Hex.encodeHexString(byteBuffer);
    }

    /*--------------------AES128------------------*/
    private static final String KEY_ALGORITHM = "AES";
    private static final String CHAR_SET = "UTF-8";
    /**
     * 加解密算法/工作模式/填充方式
     */
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";

    /**
     * AES加密操作
     *
     * @param content  待加密内容
     * @param password 加密密码
     * @return 返回Base64转码后的加密数据
     */
    public static String encrypt(String content, String password) {
        try {
            //创建密码器
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            byte[] byteContent = content.getBytes(CHAR_SET);
            //初始化为加密密码器
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));
            return Hex.encodeHexString(cipher.doFinal(byteContent));
        } catch (Exception e) {
            throw new BaseException("加密数据失败");
        }
    }

    /**
     * AES解密操作
     *
     * @param encryptContent 加密的密文
     * @param password       解密的密钥
     * @return
     */
    public static String decrypt(String encryptContent, String password) {
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            //设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password));
            //执行解密操作
            byte[] result = cipher.doFinal(Hex.decodeHex(encryptContent.toCharArray()));
            return new String(result);
        } catch (Exception e) {
            throw new BaseException("解密数据失败");
        }
    }

    /**
     * 生成秘钥
     *
     * @param password 原始秘钥
     */
    private static SecretKeySpec getSecretKey(final String password) throws UnsupportedEncodingException {
        byte[] raw = password.getBytes(StandardCharsets.UTF_8);
        return new SecretKeySpec(raw, KEY_ALGORITHM);
    }


}
